use anyhow::Result;
use embedded_svc::mqtt::client::{Event::Received, QoS};
use esp_idf_svc::{
    eventloop::EspSystemEventLoop,
    hal::{
        delay,
        i2c::{I2cConfig, I2cDriver},
        prelude::*,
    },
    mqtt::client::{EspMqttClient, MqttClientConfiguration},
};
use log::{info, warn};
use mqtt_messages::{hello_topic};
use rgb_led::{RGB8, WS2812RMT};
use shtcx::{self, shtc3, PowerMode};
use std::{thread::sleep, time::Duration};
use esp_idf_svc::wifi::EspWifi;
use serde_json::json;
use wifi::wifi;

const UUID: &str = "esp32c3rust";
const MANUFACTURE: &str = "ESPRESSIF";
const SERIAL_NO: &str = "1";
const MODEL_NAME: &str = "ESP32C3RS";

#[toml_cfg::toml_config]
pub struct Config {
    #[default("10.1.1.199")]
    mqtt_host: &'static str,
    #[default("")]
    mqtt_user: &'static str,
    #[default("")]
    mqtt_pass: &'static str,
    #[default("")]
    wifi_ssid: &'static str,
    #[default("")]
    wifi_psk: &'static str,
}

fn main() -> Result<()> {
    esp_idf_svc::sys::link_patches();
    esp_idf_svc::log::EspLogger::initialize_default();

    let peripherals = Peripherals::take().unwrap();
    let sysloop = EspSystemEventLoop::take()?;

    // The constant `CONFIG` is auto-generated by `toml_config`.
    let app_config = CONFIG;

    let pins = peripherals.pins;
    let sda = pins.gpio10;
    let scl = pins.gpio8;
    let mut led = WS2812RMT::new(pins.gpio2, peripherals.rmt.channel0)?;
    // led.set_pixel(RGB8::new(1, 1, 0))?;
    led.set_pixel(RGB8::new(50, 50, 0))?;


    // Connect to the Wi-Fi network
    let wifi = wifi(
        app_config.wifi_ssid,
        app_config.wifi_psk,
        peripherals.modem,
        sysloop,
    )?;

    led.set_pixel(RGB8::new(0, 0, 50))?;

    info!("Our UUID is: {UUID}");
    let imei = imei::generate();
    info!("imei: {}", imei);


    let i2c = peripherals.i2c0;
    let config = I2cConfig::new().baudrate(100.kHz().into());
    let i2c = I2cDriver::new(i2c, sda, scl, &config)?;
    let mut temp_sensor = shtc3(i2c);
    let mut delay = delay::Ets;


    let mqtt_config = MqttClientConfiguration::default();

    let broker_url = if !app_config.mqtt_user.is_empty() {
        format!(
            "mqtt://{}:{}@{}",
            app_config.mqtt_user, app_config.mqtt_pass, app_config.mqtt_host
        )
    } else {
        format!("mqtt://{}", app_config.mqtt_host)
    };

    led.set_pixel(RGB8::new(0, 50, 0))?;

    // 1. Create a client with default configuration and empty handler
    let mut client =
        EspMqttClient::new(
            &broker_url,
            &mqtt_config,
            move |message_event| match message_event {
                // Ok(Received(msg)) => process_message(msg, &mut led),
                Ok(Received(_msg)) => info!("Received Message"),
                _ => warn!("Received from MQTT: {:?}", message_event),
            },
        )?;

    // 2. publish an empty hello message
    let payload: &[u8] = &[];
    let topic = &hello_topic(UUID);
    info!("topic {topic}");
    client.publish(topic, QoS::AtLeastOnce, true, payload)?;

    client.subscribe(&mqtt_messages::color_topic(UUID), QoS::AtLeastOnce)?;

    loop {
        led.set_pixel(RGB8::new(20, 25, 30))?;
        sleep(Duration::from_millis(500));
        let temp = temp_sensor
            .measure_temperature(PowerMode::NormalMode, &mut delay)
            .unwrap()
            .as_degrees_celsius();
        // 3. publish CPU temperature
        info!("temp: {temp}");
        let payload = build_payload(temp, &imei, &wifi);
        client.publish(
            &mqtt_messages::temperature_data_topic(UUID),
            QoS::AtLeastOnce,
            false,
            // &temp.to_be_bytes() as &[u8],
            payload.as_bytes(),
        )?;
        led.set_pixel(RGB8::new(50, 50, 50))?;
        sleep(Duration::from_millis(500));
    }
}

fn build_payload(temp: f32, imei: &str, wifi: &Box<EspWifi>) -> String {
    let sta_netif = wifi.sta_netif();
    let mac = sta_netif.get_mac().unwrap_or_default();
    let mac_strings: Vec<String> = mac.iter().map(|&num| num.to_string()).collect();
    let json = json!({
        "imei": imei,
        "temp": temp.to_string(),
        "ip": sta_netif.get_ip_info().unwrap().ip.to_string(),
        "mac": mac_strings.join(":"),
        "manufacture": MANUFACTURE,
        "serialNo": SERIAL_NO,
    });
    json.to_string()
}

// fn process_message(message: &EspMqttMessage, led: &mut WS2812RMT) {
//     match message.details() {
//         Complete => {
//             info!("{:?}", message);
//             let message_data: &[u8] = message.data();
//             if let Ok(ColorData::BoardLed(color)) = ColorData::try_from(message_data) {
//                 info!("{}", color);
//                 if let Err(e) = led.set_pixel(color) {
//                     error!("Could not set board LED: {:?}", e)
//                 };
//             }
//         }
//         _ => error!("Could not set board LED"),
//     }
// }
